package com.xuanyue.db.xuan.core.index;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import com.xuanyue.db.xuan.core.exception.IndexException;
import com.xuanyue.db.xuan.core.exception.SQLException;
import com.xuanyue.db.xuan.core.table.IBitIndex;
import com.xuanyue.db.xuan.core.table.IColumn;
import com.xuanyue.db.xuan.core.table.INumberColumn;
import com.xuanyue.db.xuan.core.table.ISortElement;
import com.xuanyue.db.xuan.core.table.expr.Expr;
import com.xuanyue.db.xuan.core.table.expr.ExprValue;
import com.xuanyue.db.xuan.core.table.expr.ExprValueType;
import com.xuanyue.db.xuan.core.table.expr.SortTemplate;
import com.xuanyue.db.xuan.core.tools.Savor;
import com.xuanyue.db.xuan.msg.VLAUETYPE;
import com.xuanyue.db.xuan.msg.X2YValue;
import com.xuanyue.db.xuan.utils.StringUtils;
/**
 * 有符号整数。
 * 这里数值的符号不是用符号位来表示的，因为用符号位来表示数值正负，在排序时会非常复杂，所以这里采用   无符号数值  加  偏移量  的方式。
 * zero： 定义0点坐标。  就是偏移量。
 *        原始值 = data-zero
 * 例如  zero 设置为120（十进制一百）： 
 *      原始值 = 0      data= 120
 *      原始值 = 120    data= 240
 * @author 解观海
 * @email  guanhaixie@sina.cn
 * @date 2020年6月23日
 * @version 0.1
 */
public class NumberIndex implements INumberColumn<Long>{

	
	private long zero;//1:+ 0:-
	private UNumberIndex data;
	private String path;
	@Override
	public void setPath(String path) {
		this.path = path;
		data.setPath(String.format("%s/data",path));
	}
	private long pow(int x,int exponent) {
		long r = 1;
		for(int i=0;i<exponent;i++) {
			r*=x;
		}
		return r;
	}
	
	
	@Override
	public boolean modify(IColumn newOne,boolean cpDataFromOld) {
		NumberIndex one = (NumberIndex)newOne;
		if(cpDataFromOld) {
			File cfcFile = new File(String.format("%s/ColumnValueFileCache__", path));
			ColumnValueFileCache cfc = new ColumnValueFileCache();
			cfc.init(cfcFile);
			try {
				cfc.open();
				BitIndexIterator ite = new BitIndexIterator(data.getMask());
				ExprValue ev = new ExprValue();
				ev.setType(ExprValueType.LONG);
				int id;
				long v;
				int count = 0;
				while(ite.hasNext()) {
					id = ite.next();
					v=this.get(id);
					cfc.write(id);
					cfc.write(v);
					count++;
				}
				this.data.modify(one.data,false);
				this.zero=one.zero;
				Savor.write(zero,String.format("%s/zero",path));
				cfc.seek(0);
				for(int i=0;i<count;i++) {
					id = cfc.readInt();
					v=cfc.readLong();
					ev.setValue(v);
					this.set(id, ev);
				}
				return true;
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					cfc.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}else {
			this.data.modify(one.data,false);
			this.zero=one.zero;
			Savor.write(zero,String.format("%s/zero",path));
			data.getMask().setAll(false);
		}
		return false;
	}
	
	public IBitIndex getMask() {
		return data.getMask();
	}
	public void flush(int maxId) {
		data.flush(maxId);
	}
	public NumberIndex(int size,long zero) {
		data = new UNumberIndex(size);
		this.zero=zero;
	}
	public NumberIndex(int size) {
		data = new UNumberIndex(size);
		zero=pow(2,size-1)-1;
	}
	public NumberIndex() {}
	public int getSize() {
		return data.getSize();
	}
	public long getZero() {
		return zero;
	}
	
	@Override
	public void equeals(IBitIndex cache, Number valueO) {
		if(valueO==null) {
			cache.copyFrom(getMask());
			cache.not();
			return;
		}
		data.equeals(cache,valueO.longValue()+zero);
		cache.and(getMask());
	}

	@Override
	public void greater(IBitIndex cache, IBitIndex now, Number valueO) {
		data.greater(cache, now, valueO.longValue()+zero);
		cache.and(getMask());
	}

	@Override
	public void less(IBitIndex cache, IBitIndex now,  Number valueO) {
		data.less(cache, now, valueO.longValue()+zero);
		cache.and(getMask());
	}

	@Override
	public void greaterAndEq(IBitIndex cache, IBitIndex now,  Number valueO) {
		data.greaterAndEq(cache, now, valueO.longValue()+zero);
		cache.and(getMask());
	}

	@Override
	public void lessAndEq(IBitIndex cache, IBitIndex now,  Number valueO) {
		long value = (long)valueO;
		value+=zero;
		data.lessAndEq(cache, now, value);
		cache.and(getMask());
	}

	@Override
	public void set(int rowId, ExprValue valueO) {
		
		if(ExprValueType.LONG.is( valueO.getType() )) {
			long value = valueO.getValue();
			value+=zero;
			ExprValue in = new ExprValue();
			in.setType(ExprValueType.LONG);
			in.setValue(value);
			data.set(rowId, in);
		}else if(ExprValueType.NULL.is( valueO.getType() )) {
			data.set(rowId, ExprValue.getNULLExprValue());
		}else {
			throw new SQLException("value type ERROR:"+valueO.getType()+"  is not a long value");
		}
	}

	@Override
	public Long get(int rowId) {
		if(getMask().get(rowId)) {
			long v = data.get(rowId);
			return v-zero;
		}else {
			return null;
		}
	}

	@Override
	public ExprValue getExprValue(int rowId) {
		return new ExprValue(ExprValueType.LONG, get(rowId));
	}
	
	@Override
	public void save() {
		File p = new File(path);
		if(!p.exists()) {
			p.mkdirs();
		}
		Savor.write(zero,String.format("%s/zero",path));
		if(data!=null)
			data.save();
	}

	@Override
	public void load()  {
		File p = new File(path);
		if(!p.exists()) {
			throw new IndexException(String.format("%s is not exists", path));
		}
		data.load();
		zero = Savor.read(String.format("%s/zero",path));
	}
	@Override
	public void init() {
		File p = new File(path);
		if(!p.exists()) {
			p.mkdirs();
		}
		data.init();
		Savor.write(zero,String.format("%s/zero",path));
	}
	
	@Override
	public int getDataSize() {
		return data.getDataSize();
	}

	@Override
	public List<ISortElement> getSortE(SortTemplate sortTemplate) {
		return data.getSortE(sortTemplate);
	}
	@Override
	public boolean checkSortE(SortTemplate sortTemplate) {
		return sortTemplate.isType(SortTemplate.GENERAL);
	}
	@Override
	public VLAUETYPE getType() {
		return VLAUETYPE.LONG;
	}

	@Override
	public void saveRow( int rowId) {
		data.saveRow(rowId);
	}
	@Override
	public boolean checkType(ExprValue v) {
		return ExprValueType.INT.is(v.getType())||ExprValueType.NULL.is(v.getType())||ExprValueType.LONG.is(v.getType());
	}
	@Override
	public void exe(Expr expr, List<IBitIndex> caches) {
		ExprValue ev = expr.getValue();
		if(ExprValueType.NULL.is(ev.getType())) {
			data.exe(expr, caches);
		}else if(ExprValueType.LONG.is(ev.getType())||ExprValueType.INT.is(ev.getType())){
			Number tar = ev.getValue();
			Expr ex = new Expr();
			ExprValue inV = new ExprValue();
			inV.setType(ev.getType());
			inV.setValue(  tar.longValue()+zero  );
			ex.setOp( expr.getOp() );
			ex.setValue(inV);
			data.exe(ex, caches);
		}
	}
	@Override
	public int checkAndContSource(Expr expr) {
		return data.checkAndContSource(expr);
	}
}
